package com.starsoft.trade.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.starsoft.frame.util.DateUtil;
import com.starsoft.frame.util.MapUtil;
import com.starsoft.frame.util.NumberUtil;
import com.starsoft.frame.util.StringUtil;
import com.starsoft.smdc.bean.SmdcMarketDaily;
import com.starsoft.smdc.service.HolidayService;
import com.starsoft.smdc.service.MarketDailyService;
import com.starsoft.smdc.service.SecurityService;
import com.starsoft.trade.bean.TradeAcount;
import com.starsoft.trade.bean.TradeAcountStatus;
import com.starsoft.trade.bean.TradeBill;
import com.starsoft.trade.bean.TradePosition;
import com.starsoft.trade.bean.TradePositionId;
import com.starsoft.trade.bean.TradeSummary;
import com.starsoft.trade.bean.TradeSummaryId;
import com.starsoft.trade.dao.TradeAcountDao;
import com.starsoft.trade.dao.TradeBillDao;
import com.starsoft.trade.dao.TradePositionDao;
import com.starsoft.trade.dao.TradeSummaryDao;
import com.starsoft.trade.util.TradeType;

@Service
public class AcountStatusService {

	Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	TradeAcountDao tradeAcountDao;

	@Autowired
	TradeSummaryDao summaryDao;
	
	@Autowired
	TradeSummaryService summaryService;

	@Autowired
	TradePositionDao positionDao;
	
	@Autowired
	TradePositionService positionService;

	@Autowired
	TradeBillDao billDao;

	@Autowired
	MarketDailyService marketDailyService;

	@Autowired
	HolidayService holidayService;

	@Autowired
	SecurityService securityService;

	public TradeAcountStatus getAcountStatus(Integer acountId, Date asofdate) {
		TradeAcount tradeAcount = tradeAcountDao.get(acountId);
		TradeSummaryId id = new TradeSummaryId(acountId, asofdate);
		TradeSummary summary = summaryDao.get(id);
		List<TradePosition> positions = positionDao.getPositions(acountId, asofdate);
		if (summary == null) {
			return null;
		}
		return new TradeAcountStatus(tradeAcount, summary, positions);
	}
	
	public Map<String, Object> convertBean(TradeAcountStatus status){
		Set<String> secIds = new HashSet<>();
		for (TradePosition lastPostion : status.getPositions()) {
			secIds.add(lastPostion.getId().getSecId());
		}
		Map<String, SmdcMarketDaily> marketMap = marketDailyService.getMarketMap(new ArrayList<>(secIds), status.getAsofDate());
		
		Map<String, Object> result = new HashMap<String, Object>();
		Map<String, Object>  summary = MapUtil.toMap(status.getSummary());
		TradeSummary summayOb = status.getSummary();
		summary.put("acountId", summayOb.getId().getAcountId());
		summary.put("summaryDate", summayOb.getId().getAsofdate());
		summary.put("profit", summayOb.getProfit()*100);
		summary.put("profitDay", summayOb.getProfitDay()*100);
		summary.put("posRatio", summayOb.getMarketValue()/summayOb.getNetAssert()*100);		
		result.put("summary", summary);
		
		List<TradePosition> positions = status.getPositions();
		List<Map<String, Object>> jsonPositions = new ArrayList<>();
		for (TradePosition position : positions) {
			Map<String, Object> one = MapUtil.toMap(position);
			String secId = position.getId().getSecId();
			SmdcMarketDaily market = marketMap.get(secId);
			String secName = securityService.get(secId).getSecName();
			one.put("secId", secId);
			one.put("secName", secName);
			one.put("price", market.getClosePrice());
			one.put("percent", market.getPercent() * 100);
			one.put("buyDate", position.getBuyDate());
			if(position.getSecValue()!=null){
				one.put("posRatio", position.getSecValue() / summayOb.getNetAssert() * 100);
	
				double profitMoney = position.getSecValue() - position.getTotalCost();
				double profit = 0;
				if (position.getTotalCost() > 0) {
					profit = profitMoney / position.getTotalCost() * 100;
				} else if (position.getSecValue() != 0) {
					profit = profitMoney / position.getSecValue() * 100;
				}
				double profitDay = 0;
				double shareCost = 0;
				if (position.getSecNum() != 0) {
					shareCost = position.getTotalCost() / position.getSecNum();
					profitDay = position.getProfitDayMoney() / position.getSecValue() * 100;
				}
				one.put("profitMoney", profitMoney);
				one.put("profit", profit);
				one.put("profitDay", profitDay);
				one.put("shareCost", shareCost);
			}
			jsonPositions.add(one);
		}
		result.put("positions", jsonPositions);
		return result;
	}
	
	public TradeAcountStatus updateAndGet(Integer acountId, Date asofdate) {
		TradeAcountStatus status = this.getAcountStatus(acountId, asofdate);
		if(status==null){
			status = this.updateStatus(acountId, asofdate, asofdate);
		}
		return status;
	}

	public TradeAcountStatus updateAndGetLatest(Integer acountId) {
		Date asof = holidayService.getBusinessDay(new Date(), true);
		if(holidayService.isBeforeOpen(asof)){
			asof =  holidayService.getNextBusinessDay(asof, -1);
		}
		TradeAcount tradeAcount = tradeAcountDao.get(acountId);
		if(tradeAcount.getEndDate()!=null && tradeAcount.getEndDate().before(asof)){
			asof = tradeAcount.getEndDate();
		}
		return updateAndGet(acountId, asof);
	}

	public void saveStatus(TradeAcountStatus status) {
		summaryDao.saveOrUpdate(status.getSummary());
		positionDao.deletePositions(status.getSummary().getId().getAcountId(), status.getSummary().getId()
				.getAsofdate());
		positionDao.savePositions(status.getPositions());
	}

	public void deleteAcountAll(Integer acountId) {
		summaryDao.deleteAcountAll(acountId);
		positionDao.deleteAcountAll(acountId);
	}
	
	public void deleteStatusAfter(Integer acountId, Date date) {
		summaryDao.deleteStatusAfter(acountId, date);
		positionDao.deleteStatusAfter(acountId, date);
	}
	
	public TradeAcountStatus updateStatus(Integer acountId, Date beginDate, Date endDate) {
		TradeAcount tradeAcount = tradeAcountDao.get(acountId);
		Date lastDate = summaryDao.getLastSummaryDate(acountId, beginDate);
		TradeAcountStatus lastStatus = null;
		if (lastDate == null) {
			lastStatus = updateInitStatus(acountId);
			lastDate = tradeAcount.getInitDate();
		} else {
			lastStatus = getAcountStatus(acountId, lastDate);
		}

		if (endDate == null) {
			endDate = new Date();
		}
		
		endDate = holidayService.getBusinessDay(endDate, true);
		if(holidayService.isBeforeOpen(new Date()) && DateUtil.isToday(endDate)){
			endDate =  holidayService.getNextBusinessDay(endDate, -1);
		}
		Date statusDate = holidayService.getNextBusinessDay(lastDate, 1);
		while (!statusDate.after(endDate)) {
			try {
				TradeAcountStatus status = calculateStatus(statusDate, lastStatus);
				this.saveStatus(status);
				lastStatus = status;
			} catch (Exception e) {
				String message ="计算"+DateUtil.toString(statusDate, "yyyyMMdd") +"账户状态失败:" + e.getMessage();
				throw new RuntimeException(message, e);
			}
			statusDate = holidayService.getNextBusinessDay(statusDate, 1);
		}
		return lastStatus;
	}

	@SuppressWarnings("unchecked")
	public TradeAcountStatus updateInitStatus(Integer acountId) {
		TradeAcount tradeAcount = tradeAcountDao.get(acountId);
		String cmpIndex = tradeAcount.getCmpIndex();
		if(StringUtil.isEmpty(cmpIndex)){
			cmpIndex = "000300.ss";
		}
		
		Date asofdate = tradeAcount.getInitDate();
		double cash = tradeAcount.getInitCash();

		ObjectMapper mapper = new ObjectMapper();
		List<Map<String, Object>> posList = new ArrayList<>();
		try {
			posList = mapper.readValue(tradeAcount.getInitPos(), posList.getClass());
		} catch (Exception e) {
			e.printStackTrace();
		}

		double marketValue = 0;
		List<TradePosition> positions = new ArrayList<>();
		if (posList != null) {
			for (Map<String, Object> pos : posList) {
				String secId = (String) pos.get("secId");
				Double secNum = Double.parseDouble(String.valueOf(pos.get("secNum")));
				TradePositionId positionId = new TradePositionId(acountId, asofdate, secId);
				TradePosition position = new TradePosition(positionId);
				position.setSecNum(secNum);
				SmdcMarketDaily marketDaily = marketDailyService.getMarketDaily(secId, asofdate);
				double interest =  securityService.getBondInterset(secId, asofdate);
				double secValue = (marketDaily.getClosePrice() + interest) * secNum;
				position.setSecValue(secValue);
				position.setTotalCost(secValue);
				position.setProfitDayMoney(0.0);
				position.setSecNumAfs(0.0);
				positions.add(position);
				marketValue += secValue;
			}
		}

		TradeSummaryId id = new TradeSummaryId(acountId, asofdate);
		TradeSummary summary = new TradeSummary(id);
		summary.setPrincipal(cash + marketValue);
		summary.setCash(cash);
		summary.setMarketValue(marketValue);
		summary.setNetAssert(cash + marketValue);
		SmdcMarketDaily indexMarket = marketDailyService.getMarketDailyNotNull(cmpIndex, asofdate);
		double baseValue = indexMarket.getClosePrice();
		if(indexMarket.getDivi()!=null) baseValue = indexMarket.getDivi();
		summary.setBaseValue(baseValue);
		
		summary.setProfitMoney(0.0);
		summary.setProfitDayMoney(0.0);
		summary.setProfit(0.0);
		summary.setProfitDay(0.0);
		summary.setBaseProfit(0.0);
		summary.setBaseProfitDay(0.0);

		TradeAcountStatus initStatus = new TradeAcountStatus(tradeAcount, summary, positions);
		saveStatus(initStatus);
		return initStatus;
	}

	public TradeAcountStatus calculateStatus(Date asofdate, TradeAcountStatus lastStatus) {
		Integer acountId = lastStatus.getAcountId();
		Date lastDate = DateUtil.getNext(lastStatus.getSummary().getId().getAsofdate());
		List<TradeBill> bills = billDao.getBills(acountId, lastDate, asofdate);
		return this.calculateStatus(asofdate, lastStatus, bills);
	}
	
	public TradeAcountStatus calculateStatus(Date asofdate, TradeAcountStatus lastStatus, List<TradeBill> bills) {
		Set<String> secIds = new HashSet<>();
		double cashChg = 0, principleChg = 0, marketValue = 0;
		Integer acountId = lastStatus.getAcountId();
		String cmpIndex = tradeAcountDao.get(acountId).getCmpIndex();
		if(StringUtil.isEmpty(cmpIndex)){
			cmpIndex = "000300.ss";
		}
		if (bills == null){
			bills = new  ArrayList<TradeBill>();
		}
		secIds.add(cmpIndex);
		for (TradePosition lastPostion : lastStatus.getPositions()) {
			secIds.add(lastPostion.getId().getSecId());
		}
		for (TradeBill bill : bills) {
			cashChg += NumberUtil.null2Zero(bill.getBillMoney());
			if (TradeType.get(bill) == TradeType.transfer) {
				principleChg += NumberUtil.null2Zero(bill.getBillMoney());
			}
			if(!StringUtil.isEmpty(bill.getSecId())){
				secIds.add(bill.getSecId());
			}
		}

		Map<String, SmdcMarketDaily> marketMap = marketDailyService.getMarketMap(new ArrayList<>(secIds), asofdate);
		List<TradePosition> newPositons = positionService.billToPostion(bills, marketMap);
		List<TradePosition> existPostions = positionService.copyToOtherDay(lastStatus.getPositions(), asofdate, marketMap);
		newPositons.addAll(existPostions);
		newPositons = positionService.combinePostion(newPositons);
		for(TradePosition position : newPositons){
			marketValue += position.getSecValue();
		}
		
		SmdcMarketDaily indexMarket = marketMap.get(cmpIndex);
		double baseValue = indexMarket.getClosePrice();
		if(indexMarket.getDivi()!=null) baseValue = indexMarket.getDivi();
		Date lastDay = holidayService.getNextBusinessDay(asofdate, -1);
		Date lastDay2 = lastStatus.getSummary().getId().getAsofdate();
		TradeSummary lastSummary = lastStatus.getSummary();
		if(!DateUtil.isSameDay(lastDay, lastDay2)&& !DateUtil.isSameDay(lastDay2, asofdate)){
			lastSummary = summaryDao.get(new TradeSummaryId(acountId, lastDay));
		}
		TradeSummary summary = summaryService.caculateSummary(asofdate, lastSummary, cashChg, principleChg,
				marketValue, baseValue);

		return new TradeAcountStatus(lastStatus.getTradeAcount(), summary, newPositons);
	}
	
	public TradeAcountStatus combine(TradeAcountStatus statusA, TradeAcountStatus statusB){
		TradeSummary summaryFrom = statusA.getSummary();
		TradeSummary summaryTo = statusB.getSummary();
		TradeSummary cominedSummary = summaryService.combine(summaryFrom, summaryTo);
		statusB.setSummary(cominedSummary);
		
		List<TradePosition> positions = statusA.getPositions();
		positions.addAll(statusB.getPositions());
		statusB.setPositions(positionService.combinePostion(positions));
		return statusB;
	}

}
